-- SlimeSkillM
-- created by panyl
-- 冈布奥主动技能（专属）

module("SlimeSkillM", package.seeall);

local slimeSkills = {};
local dragonMagics = {};
local allSkills  = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    slimeSkills = {};
    dragonMagic = {};
    allSkills   = {};

    local slime_skill_CSV = get_slime_skill_CSV();
    for i = 1, #slime_skill_CSV do
        local row = slime_skill_CSV[i];
        allSkills[row.id] = row;

        -- 龙语魔法跟其他魔法要区分开来
        if row.dragon_magic == 1 then
            dragonMagics[row.id] = row;
        else
            slimeSkills[row.id] = row;
        end
    end
end

-- 模块初始化操作
function init()
    loadCsv();

    -- 移除所有的事件回调
    EventMgr.removeAll("SlimeSkillM");

    EventMgr.register("SlimeSkillM", event.START_DUNGEON, function(para)
        -- 使用记录清除
        ME.user.dbase:delete("slime_skill_stat");
    end);
end

-- 检索
function query(id, path)
    local m = allSkills[id];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 通过技能ID查找slime_skill的Id
function getIdBySkill(skillId)
    for _, data in pairs(allSkills) do
        if data.cast_skill_id == skillId then
            return data.id;
        end
    end

    return nil;
end

-- 检索slime专属技能
function querySlimeSkill(id, path)
    local m = slimeSkills[id];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 计算玩家消耗
function calcUserEnergyCost(id)
    -- 扣除能量配置
    local energy = table.deepcopy(query(id, "energy"));
    local dbase = query(id, "dbase");

    -- 玩家降低消耗属性
    local prop = PropM.combine(ME.user, "slime_skill_reduce_cost", id);

    local ret = {};
    for key, cost in pairs(energy) do
        -- 如果配置了公式
        if type(dbase["changeable_cost"]) == 'string' then
            cost = FormulaM.invoke(dbase["changeable_cost"], key);
        end

        -- 玩家降低消耗属性
        cost = PropM.apply(prop, cost);

        ret[key] = cost;
    end

    return ret;
end

-- 扣除能量
function costEnergy(id)
    -- 计算消耗
    local energy = calcUserEnergyCost(id);
    for key, cost in pairs(energy) do
        if type(key) == 'number' then
            -- 消耗物品
            if not ItemM.costAmount(ME.user, key, cost) then
                trace("SlimeSkillM", "冈布奥主动技%d扣除物品%d(%d)失败\n", id, key, cost);
                return false;
            end
        else
            -- 消耗属性
            if not DungeonServiceM.costDungeonAttrib({[key] = cost}) then
                trace("SlimeSkillM", "冈布奥主动技%d扣除属性%s(%d)失败\n", id, key, cost);
                return false;
            end
        end
    end

    return true;
end

-- 获取技能等级
function getSkillLevel(id)
    -- 根据属性算
    local prop = PropM.combine(ME.user, "gain_slime_skill", id);

    return prop[3];
end

-- 当前某个技能消耗是否足够
function enoughtEnergy(id)
    -- 计算消耗
    local energy = calcUserEnergyCost(id);
    for key, cost in pairs(energy) do
        local amount = 0;
        if type(key) == 'number' then
            -- 消耗物品
            amount = ItemM.getAmount(ME.user, key);
        else
            -- 消耗能量
            amount = DungeonServiceM.queryDungeonAttrib(key);
        end

        if amount < cost then
            return false;
        end
    end

    return true;
end

-- 判断能否施放
function canApply(targetPos, id)
    -- 如果玩家没有此技能(如果是冈布奥专属技能需要判断有么有当前技能)
    local isDragonMagic = query(id, "dragon_magic");
    if isDragonMagic ~= 1 and getSkillLevel(id) <= 0 then
        trace("SlimeSkillM", "没有技能%s。", query(id, "name"));
        return false;
    end

    -- 目标是否怪物
    if type(targetPos) == "number" and targetPos > 0 then
        local grid = DungeonM.getGridByPos(targetPos);
        if not grid.monster then
            trace("SlimeSkillM", "位置%d不是怪物。", targetPos);
            return false;
        end
    end

    -- 本层使用次数
    local dbase = query(id, "dbase");
    local use_stat = ME.user.dbase:query("slime_skill_stat", {});
    local use_times = dbase["use_times"];
    if use_times and tonumber(use_stat[id]) >= use_times then
        return getLocStr("slime_skill_too_often");
    end

    -- 计算消耗
    local energy = calcUserEnergyCost(id);
    for key, cost in pairs(energy) do
        local amount = 0;
        local name;
        if type(key) == 'number' then
            -- 消耗物品
            amount = ItemM.getAmount(ME.user, key);
            name = ItemM.query(key, "name");
        else
            -- 消耗能量
            amount = DungeonServiceM.queryDungeonAttrib(key);
            name = FieldsM.getFieldName(key) or key;
        end

        if amount < cost then
            return string.format(getLocStr("lack_skill_energy"), name);
        end
    end

    -- 这个技能是否能释放
    local skillId = query(id, "cast_skill_id");
    local ret = SkillM.canCast(ME.user, skillId, targetPos);
    if true ~= ret then
        if type(ret) == "string" then
            if ret == "forbid_magic" then
                -- 处于禁魔状态
                return getLocStr("forbid_magic_tip");
            elseif ret == "forbidden_magic" then
                -- 处于禁魔状态
                return getLocStr("forbidden_magic_tip");
            elseif ret == "blood_poisoned" then
                -- 中了血毒
                return getLocStr("failed_reason_blood_poisoned");
            elseif ret == "dark_ceremony" then
                -- 暗咒
                return getLocStr("failed_reason_dark_ceremony");
            elseif getLocStr(ret) ~= ret then
                return getLocStr(ret);
            else
                -- 拦截后的提示
                local msg = FormulaM.invoke("CALC_FORBID_SKILL_ALERT", ret);
                if msg ~= "" then
                    return msg;
                end
            end
        elseif type(ret) == "table" then
            if ret[1] == "summon_alive" then
                -- 召唤兽还存活着
                local summonId = ret[2];
                return string.format(getLocStr("do_summon_fail"), SummonM.query(summonId, "name"));
            end
        end

        trace("SlimeSkillM", "经判断，不能使用技能%s。", query(id, "name"));
        return false;
    end

    return true;
end

-- 施放技能
function apply(targetPos, skillId)
    -- 先根据skillId找到id
    local id = getIdBySkill(skillId);
    if not id then
        return false;
    end

    -- 先判断能否施放
    if true ~= canApply(targetPos, id) then
        return false;
    end

    -- 先扣除消耗
    costEnergy(id);

    -- 如果是龙语魔法直接使用配置等级
    local level;
    local isDragonMagic = query(id, "dragon_magic");
    if isDragonMagic == 1 then
        level = SkillM.query(skillId, "level");
    else
        level = getSkillLevel(id);
    end

    -- 施放技能
    SkillM.cast(ME.user, skillId, targetPos, {["level"] = level,});

    DungeonM.addAction({["cmd"] = "use_slime_skill", ["pos"] = targetPos, ["data"] = skillId, });

    -- 抛出使用特殊技能事件
    EventMgr.fire(event.USE_SLIME_SKILL, { ["skillId"] = skillId, });

    -- 某些技能需要延时一下
    -- TODO: 等播放序列的流合过来后，需要修改
    local isDelay;
    if skillId == 114 then
        isDelay = true;
    end

    -- 统计本层使用次数
    local dbase = query(id, "dbase") or {};
    if dbase["use_times"] then
        local stat = ME.user.dbase:query("slime_skill_stat", {});
        stat[id] = tonumber(stat[id]) + 1;
        ME.user.dbase:set("slime_skill_stat", stat);
    end

    local dbase = query(id, "dbase");
    -- 如果强制同步的不抛事件
    if dbase["force_sync"] ~= 1 and dbase["no_combat_round"] ~= 1 then
        -- 一个回合事件
        EventMgr.fire(event.COMBAT_ROUND, { ["pos"] = targetPos, ["isDelay"] = true, });
    end

    -- 像时光回溯这种技能，需要直接同步
    if dbase["force_sync"] == 1 then
        if dbase["save_dungeon"] then
            -- 保存下当前的地牢数据
            DungeonM.addAction({ ["cmd"] = "save_dungeon", });
        end

        DungeonM.sync();
    end

    return true, true;
end

-- 获取所有的主动技能(冈布奥的主动技能)
function getAllSkills()
    local props = PropM.fetchProps(ME.user, "gain_slime_skill");
    local ret = {};

    for _, prop in ipairs(props) do
        local id = tonumber(prop[2]);
        if getSkillLevel(id) > 0 then
            table.insert(ret, query(id, "cast_skill_id"));
        end
    end

    return ret;
end

-- 获取当前某冈布奥的所有主动技能
function getSamePetSkills(petId)
    local ret = {};
    local skills = getAllSkills();
    for _, skillId in pairs(skills) do
        local slimeSkillId = SlimeSkillM.getSlimeActiveSkillId(skillId);
        if petId == query(slimeSkillId, "pet_id") then
            table.insert(ret, skillId);
        end
    end
    return ret;
end

-- 根据skillId获取id
function getSlimeActiveSkillId(skillId)
    for _, data in pairs(slimeSkills) do
        if data.cast_skill_id == skillId then
            return data.id;
        end
    end
end

-- 判断是否冈布奥主动技能
function isSlimeSkill(skillId)
    local id = getSlimeActiveSkillId(skillId);

    if id then
        return true;
    else
        return false;
    end
end

-- 无视免疫
function defyImmunitySkill(skillId)
    -- 如果不是slime_skill
    if not isSlimeSkill(skillId) then
        return false;
    end

    local id = getSlimeActiveSkillId(skillId);
    local dbase = query(id, "dbase");

    return 1 ~= dbase["can_be_immunity"];
end

-- 获取消耗描述
function getCostDesc(skillId)
    -- 消耗%s：%d
    local costDesc = getLocStr("skill_cost_desc");
    local costValue = 0;
    local costName = getLocStr("skill_cost_energy");
    local slimeSkillId = getIdBySkill(skillId);

    local costInfo = query(slimeSkillId, "energy");
    local extraInfo = query(slimeSkillId, "dbase");

    -- 获取消耗道具名称及消耗数量
    for costItem, value in pairs(costInfo) do
        if type(costItem) == "number" then
            -- 是道具
            costName = ItemM.query(costItem, "name");

            if extraInfo.cost_all == true then
                -- 消耗全部，取总值
                costValue = ItemM.getAmount(ME.user, costItem);
            elseif type(extraInfo.changeable_cost) == 'string' then
                -- 消耗值会变化，用公式计算
                costValue = FormulaM.invoke(extraInfo.changeable_cost, slimeSkillId)
            else
                costValue = value;
            end
        else
            costName = DungeonServiceM.getDungeonAttribInfo(costItem, "name");

            if not costName then
                costName = FieldsM.getFieldName(costItem) or costItem;
            end

            if extraInfo.cost_all == 1 then
                -- 消耗全部，取总值
                costValue = DungeonServiceM.queryDungeonAttrib(costItem);

                if costValue == 0 then
                    costValue = ME.user:queryAttrib(costItem);
                end
            elseif type(extraInfo.changeable_cost) == 'string' then
                -- 消耗值会变化，用公式计算
                costValue = FormulaM.invoke(extraInfo.changeable_cost)
            else
                costValue = value;
            end
        end
    end

    -- 消耗低于0时不显示
    if costValue <= 0 then
        return false;
    end

    costDesc = string.format(getLocStr("skill_cost_format"), costName, costValue);

    return costDesc;
end

-- 根据skillId获取id
function getDragonMagicSkillId(skillId)
    for _, data in pairs(dragonMagics) do
        if data.cast_skill_id == skillId then
            return data.id;
        end
    end

    return nil;
end

-- 判断是否是龙语魔法
function isDragonMagic(skillId)
    local id = getDragonMagicSkillId(skillId);

    if id then
        return true;
    else
        return false;
    end
end

-- 获取所有的主动技能
function getAllDragonMagic()
    local ret = {};

    -- 如果学习了龙语魔法，则所有的龙语魔法都可以显示
    if DungeonM.getDungeonMixedField("dragon_magic") == 1 then
        for k, v in pairs(dragonMagics) do
            table.insert(ret, v.cast_skill_id);
        end
    end

    return ret;
end

-- 技能播放完后是否需要延迟刷新UI
function isNeedDelay(skillId)
    local id = getSlimeActiveSkillId(skillId);
    if query(id, "need_delay") == 1 then
        return true;
    end

    return false;
end
